// Amf3Input.cpp: implementation of the Amf3Input class.
//
//////////////////////////////////////////////////////////////////////
//#include "stdafx.h"
#include "amf3_input.h"


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
namespace Amf {
	Amf3Input::Amf3Input(const unsigned char* pData, int32 iSize)
	{
		m_pData = (unsigned char*)pData;
		if(m_pData == NULL)
			m_iSize = 0;
		else
			m_iSize = iSize;
		
		m_iIndex = 0;
	}

	Amf3Input::~Amf3Input()
	{

	}
	
	AmfObjectPtr Amf3Input::readObjectValue(int32 type)
	{
		AmfObjectPtr value;
		
        switch (type)
        {
		case AMF3_STRING:
			{
				value = readString();
			}
			break;
			
		case AMF3_OBJECT:
			{
				value = readScriptObject();
			}
			break;
			
		case AMF3_ARRAY:
			{
				value = readArray();
			}
			break;
			
		case AMF3_FALSE:
			{
				AmfBoolPtr Value(HP_NEW);
				Value->SetValue(false);
				value = Value;
			}
			break;
			
		case AMF3_TRUE:
			{
				AmfBoolPtr Value(HP_NEW);
				Value->SetValue(true);
				value = Value;
			}
			break;
			
		case AMF3_INTEGER:
			{
				int32 i = readUInt29();
				// Symmetric with writing an integer to fix sign bits for negative values...
				i = (i << 3) >> 3;
				AmfIntPtr Value(HP_NEW);
				Value->SetValue(i);
				value = Value;
			}
			break;
			
		case AMF3_NUMBER:
			{
				AmfDoublePtr Value(HP_NEW);
				Value->SetValue(readDouble());
				value = Value;
			}
			break;
			
		case AMF3_UNDEFINED:
			break;
			
		case AMF3_NULL:
			break;
			
		case AMF3_XML:
		case AMF3_XMLSTRING:
			//value = readXml();
			break;
			
		case AMF3_DATE:
			value = readDate();
			break;
			
		case AMF3_BYTEARRAY:
			value = readByteArray();
			break;
		default:
			// Unknown object type tag {type}
			;
        }
		
        return value;
	}
	
	AmfStringPtr Amf3Input::readString()
	{
		int32 ref = readUInt29();

		if ((ref & 1) == 0)
        {
            // This is a reference
			return getStringReference(ref >> 1);
        }
        else
        {
            // Read the string in
            int32 len = (ref >> 1);
			
            // writeString() special cases the empty string
            // to avoid creating a reference.
            if (0 == len)
            {
                //return NULL;
				AmfStringPtr strNull(HP_NEW);
				strNull->SetValue(L"");
				return strNull;
            }
			
            AmfStringPtr str = readUTF(len);
			
			if(str == NULL)
			{
				AmfStringPtr strNull(HP_NEW);
				strNull->SetValue(L"");
				return strNull;
			}

            // Remember String
            stringTable.push_back(str);
			
            return str;
        }
	}

	AmfObjectPtr Amf3Input::readDate()
	{
		int ref = readUInt29();

		if ((ref & 1) == 0)
		{
			// This is a reference
			return getObjectReference(ref >> 1);
		}
		else
		{
			float64 time = readDouble();

			MilliTime mstime((int32)(time / 1000), (int64)time % 1000);
			AmfDatePtr DatePtr(HP_NEW);
			DatePtr->SetValue(mstime);

			//Remember Date
			objectTable.push_back(DatePtr);

			return DatePtr;
		}
	}
		
	AmfObjectPtr Amf3Input::readArray()
	{
		int32 ref = readUInt29();
		
        if ((ref & 1) == 0)
        {
            // This is a reference
            return getObjectReference(ref >> 1);
        }
        else
		{
			int32 len = (ref >> 1);
            AmfObjectPtr array;
			
			// First, look for any string based keys. If any
            // non-ordinal indices were used, or if the Array is
            // sparse, we represent the structure as a Map.
            AmfMapPtr map;
            for (; ;)
            {
                AmfStringPtr name = readString();
                if (name->GetValue() == NULL || wcslen(name->GetValue()) == 0) break;
				
                if (map == NULL)
                {
                    map = AmfMapPtr(HP_NEW);
                    array = map;
					
                    //Remember Object
                    objectTable.push_back(array);
				}
				
                AmfObjectPtr value = readObject();
                map->AddData(name->GetValue(), value);
			}

			// If we didn't find any string based keys, we have a
            // dense Array, so we represent the structure as a List.
            if (map == NULL)
            {
				AmfArrayPtr objarray(HP_NEW);
 				array = objarray;

				// Remember List
				objectTable.push_back(array);

				for (int32 i = 0; i < len; i++)
				{
					AmfObjectPtr item = readObject();
					objarray->AddData(item);
				}
            }
			else
            {
				for (int32 i = 0; i < len; i++)
                {					
					AmfObjectPtr item = readObject();
					
					wstringstream buf;
					buf << i;
					map->AddData(buf.str(), item);

					// old implement
//					wchar_t buffer[20];
//					_itow(i, buffer, 10);				
//					wstring str = buffer;
//					map->AddData(str, item);
                }
			}
			
			return array;
		}
	}
	
	AmfObjectPtr Amf3Input::readScriptObject()
	{
		int32 ref = readUInt29();
		
        if ((ref & 1) == 0)
        {
            return getObjectReference(ref >> 1);
        }
        else
		{
			TraitsInfo ti = readTraits(ref);
			wstring className = ti.getClassName();

			bool externalizable = ti.isExternalizable();

			AmfAsObjectPtr object(HP_NEW);
			
			object->SetName(className.c_str());

			// Remember our instance in the object table
			objectTable.push_back(object);

			int32 len = ti.getProperties().size();
			
			for (int32 i = 0; i < len; i++)
			{
				wstring propName = ti.getProperty(i);
								
				AmfObjectPtr value = readObject();
				object->AddData(propName, value);
			}
			
			if (ti.isDynamic())
			{
				for (; ;)
				{
					AmfStringPtr name = readString();
					if (name->GetValue() == NULL || wcslen(name->GetValue()) == 0) break;
										
					AmfObjectPtr value = readObject();
					object->AddData(name->GetValue(), value);
				}
			}

			return object;
		}
	}
	
//	void Amf3Input::readExternalizable(string className, Object object);
	
	AmfByteArrayPtr Amf3Input::readByteArray()
	{
		int32 ref = readUInt29();
		
        if ((ref & 1) == 0)
        {
            return (AmfByteArrayPtr)getObjectReference(ref >> 1);
        }
        else
        {
            int32 len = (ref >> 1);
			
            StdByteArray ary;
//			ary.resize(len);
			
//			memcpy(&ary[0], m_pData + m_iIndex, len);
			ary.insert(ary.end(), m_pData + m_iIndex, m_pData + m_iIndex + len);
			m_iIndex += len;
		
			AmfByteArrayPtr Value(HP_NEW);
			Value->SetValue(ary);

            // Remember byte array object
            objectTable.push_back(Value);

            return Value;
        }		
	}
	
	TraitsInfo& Amf3Input::readTraits(int32 ref)
	{
		if ((ref & 3) == 1)
        {
            // This is a reference
            return getTraitReference(ref >> 2);
        }
        else
        {
            bool externalizable = ((ref & 4) == 4);
            bool dynamic = ((ref & 8) == 8);
            int32 count = (ref >> 4); /* uint29 */
            AmfStringPtr className = readString();
			
            TraitsInfo ti(className->GetValue(), dynamic, externalizable);
			
			
            for (int32 i = 0; i < count; i++)
            {
                AmfStringPtr propName = readString();
                ti.addProperty(propName->GetValue());
            }

            // Remember Trait Info
            traitsTable.push_back(ti);

			int32 iEnd = traitsTable.size();
			
            return traitsTable[iEnd - 1];
        }
	}
	
	AmfStringPtr Amf3Input::readUTF(int32 utflen)
	{
		wstring ws = U8ToW((const char *)(m_pData + m_iIndex), utflen);
		m_iIndex += utflen;
		return ToAmf(ws);
	}
	
	int32 Amf3Input::readUInt29()
	{
		int32 value;
		
        // Each byte must be treated as unsigned
        int32 b = readByte() & 0xFF;
		
        if (b < 128)
        {
            return b;
        }
		
        value = (b & 0x7F) << 7;
        b = readByte() & 0xFF;
		
        if (b < 128)
        {
            return (value | b);
        }
		
        value = (value | (b & 0x7F)) << 7;
        b = readByte() & 0xFF;
		
        if (b < 128)
        {
            return (value | b);
        }
		
        value = (value | (b & 0x7F)) << 8;
        b = readByte() & 0xFF;
		
        return (value | b);		
	}
		
}// namespace Amf
